package com.jintian.smart.kernel.flowable.config;

import com.jintian.smart.kernel.autoconfigure.beetl.EnableBeetlMapper;
import com.jintian.smart.kernel.common.util.StringUtil;
import com.jintian.smart.kernel.core.annotation.flow.FlowActionMoment;
import com.jintian.smart.kernel.core.annotation.flow.FlowEvent;
import com.jintian.smart.kernel.core.annotation.flow.FlowEventTrigger;
import com.jintian.smart.kernel.core.auth.IOperatorService;
import com.jintian.smart.kernel.core.flow.IFlowEventListener;
import com.jintian.smart.kernel.flowable.engine.DefaultActionInvoker;
import com.jintian.smart.kernel.flowable.flowable.agenda.CustomFlowableEngineAgenda;
import com.jintian.smart.kernel.flowable.flowable.interceptor.CustomIdentityLinkInterceptor;
import com.jintian.smart.kernel.flowable.flowable.listener.GlobalFlowableEventListener;
import com.jintian.smart.kernel.flowable.listener.FlowEventListenerManager;
import com.jintian.smart.kernel.flowable.web.FlowAgentWebInvokeHandler;
import org.apache.dubbo.common.utils.AnnotationUtils;
import org.flowable.spring.SpringProcessEngineConfiguration;
import org.flowable.spring.boot.EngineConfigurationConfigurer;
import org.flowable.spring.boot.condition.ConditionalOnProcessEngine;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.Arrays;
import java.util.Map;

@Configuration(proxyBeanMethods = false)
@ConditionalOnProcessEngine
@ConditionalOnProperty(name = "flowable.process.enabled", havingValue = "true", matchIfMissing = true)
//@ConditionalOnExpression("#{!'false'.equals(environment['mda.masterContext'])}")
@EnableBeetlMapper(basePackages = "com.jintian.smart.kernel.flowable.mapper")
public class FlowableStarter {
    /**
     * 全局监听
     *
     * @return
     */
    @Bean
    GlobalFlowableEventListener globalFlowableEventListener() {
        return new GlobalFlowableEventListener();
    }

    @Bean
    FlowAgentWebInvokeHandler flowAgentWebInvokeHandler(IOperatorService operatorService) {
        return new FlowAgentWebInvokeHandler(operatorService);
    }

    /**
     * 添加代理用户拦截
     *
     * @return
     */
    @Bean
    WebMvcConfigurer agentWebInvokeHandlerConfigurer(FlowAgentWebInvokeHandler flowAgentWebInvokeHandler) {
        return new WebMvcConfigurer() {
            @Override
            public void addInterceptors(InterceptorRegistry registry) {
                registry.addWebRequestInterceptor(flowAgentWebInvokeHandler);
            }
        };
    }

    @Bean
    FlowEventListenerManager flowEventListenerManager() {
        return new FlowEventListenerManager();
    }

    /**
     * 流程函数注册器
     *
     * @return
     */
    @Bean
    FlowFunctionRegister flowFunctionRegister() {
        return new FlowFunctionRegister();
    }

    /**
     * Engine初始化
     *
     * @param globalFlowableEventListener
     * @return
     */
    @Bean
    EngineConfigurationConfigurer<SpringProcessEngineConfiguration> engineConfigurationConfigurer(
            @Autowired GlobalFlowableEventListener globalFlowableEventListener) {
        return new EngineConfigurationConfigurer<SpringProcessEngineConfiguration>() {
            @Override
            public void configure(SpringProcessEngineConfiguration engineConfiguration) {
                engineConfiguration.setIdentityLinkInterceptor(new CustomIdentityLinkInterceptor());
                engineConfiguration.setAgendaFactory(new CustomFlowableEngineAgenda());
                engineConfiguration.setEventListeners(Arrays.asList(globalFlowableEventListener));
                engineConfiguration.setEnableProcessDefinitionInfoCache(true);
            }
        };
    }

    /**
     * 业务动作执行器
     *
     * @return
     */
    @Bean
    DefaultActionInvoker flowActionInvoker() {
        return new DefaultActionInvoker();
    }

    @Configuration(proxyBeanMethods = false)
    @ComponentScan("com.jintian.smart.kernel.flowable")
    static class RegisterFlowEvent {

//        @SuppressWarnings("rawtypes")
//        @Autowired
//        private IFlowEventListenerRepository flowEventListenerRepository;

        @Autowired
        private FlowEventListenerManager flowEventListenerManager;

        /**
         * 扫描所有流程事件
         *
         * @param event
         */
        @EventListener
        @SuppressWarnings({"rawtypes", "unchecked"})
        public void onApplicationEvent(ContextRefreshedEvent event) {
            String all = "([\\w-])+";
            Map<String, IFlowEventListener> map = event.getApplicationContext().getBeansOfType(IFlowEventListener.class);
            for (IFlowEventListener listener : map.values()) {
                // TODO 解决有代理的情况下，无法获取注解问题
                // FlowEvent e = listener.getClass().getAnnotation(FlowEvent.class);
                FlowEvent e = AnnotationUtils.findAnnotation(listener.getClass(), FlowEvent.class);
                if (e != null) {
                    String process = all;
                    String activity = all;
                    String action = all;
                    String moment = all;
                    FlowEventTrigger[] triggers = e.value();
                    if (triggers.length == 0) {
                        flowEventListenerManager.registerListener(process, activity, action, moment, listener);
                    } else {
                        for (FlowEventTrigger trigger : triggers) {
                            if (StringUtil.isNotBlank(trigger.process())) {
                                process = "(" + trigger.process() + ")";
                            }
                            if (StringUtil.isNotBlank(trigger.activity())) {
                                activity = "(" + trigger.activity() + ")";
                            }
                            FlowActionMoment[] actionMoments = trigger.actionMoment();
                            if (actionMoments.length == 0) {
                                flowEventListenerManager.registerListener(process, activity, action, moment, listener);
                            } else {
                                for (FlowActionMoment actionMoment : actionMoments) {
                                    if (StringUtil.isNotBlank(actionMoment.action())) {
                                        action = "(" + actionMoment.action() + ")";
                                    }
                                    FlowActionMoment.Moment[] moments = actionMoment.moment();
                                    if (moments.length == 0) {
                                        flowEventListenerManager.registerListener(process, activity, action, moment, listener);
                                    } else {
                                        for (FlowActionMoment.Moment m : moments) {
                                            moment = "(" + m.name() + ")";
                                            flowEventListenerManager.registerListener(process, activity, action, moment, listener);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
